#include <string.h>

#include "els_vm.h"
#include "els_vm_bytecode.h"
#include "hash.h"
#include "unit.h"
#include "els.h"
#define REHASH_LIMIT	0.70
#define streq(s1,s2)	(s1 == s2 || (*(s1) == *(s2) && strcmp(s1,s2)==0))
#define nhash(t)	    ((t)->nhash)
#define nuse(t)		    ((t)->nuse)
#define markarray(t)  ((t)->mark)
#define node_vec(t)	((t)->node)
#define node(t,i)	    (&(t)->node[i])
#define ref(n)		    (&(n)->ref)
#define val(n)		    (&(n)->val)


static Hash *listhead = NULL;




static Word dimensions[] = {
    3, 5, 7, 11, 23,
    47, 97, 197, 397, 
    797, 1597, 3203, 6421,
    12853, 25717, 51437, 65521,0
};

static Word redimension (Word nhash){
    Word i;
    for (i=0; dimensions[i]!=0; i++){
        if (dimensions[i] > nhash)
        return dimensions[i];
    }
    els_error("空间单元溢出");
    return 0; 
}

static Word hashindex (Hash *t, Object *ref){
    switch (tag(ref)){
        case ELS_TYPE_NULL:
            els_error ("索引单元类型错误");
            return -1;  
        case ELS_TYPE_NUMBER:
            return (((Word)nvalue(ref))%nhash(t));
        case ELS_TYPE_STRING:{
            unsigned long h = tsvalue(ref)->hash;
            if (h == 0){
                char *name = svalue(ref);
                while (*name) h = ((h<<5)-h)^(unsigned char)*(name++);
                tsvalue(ref)->hash = h;
            }
            return (Word)h%nhash(t); 
        }
        case ELS_TYPE_FC:  return ((IntPoint)ref->value.tf)%nhash(t);
        case ELS_TYPE_CERRORPI: return ((IntPoint)fvalue(ref))%nhash(t);
        case ELS_TYPE_ARRAY:     return ((IntPoint)avalue(ref))%nhash(t);
        default:                 return ((IntPoint)uvalue(ref))%nhash(t);
    }
}

Bool els_equalObj (Object *t1, Object *t2){
    if (tag(t1) != tag(t2)) return 0;
    switch (tag(t1)){
        case ELS_TYPE_NULL: return 1;
        case ELS_TYPE_NUMBER: return nvalue(t1) == nvalue(t2);
        case ELS_TYPE_STRING: return streq(svalue(t1), svalue(t2));
        case ELS_TYPE_ARRAY: return avalue(t1) == avalue(t2);
        case ELS_TYPE_FC: return t1->value.tf == t2->value.tf;
        case ELS_TYPE_CERRORPI: return fvalue(t1) == fvalue(t2);
        default: return uvalue(t1) == uvalue(t2);
    }
}

static Word present (Hash *t, Object *ref){ 
    Word h = hashindex(t, ref);
    while (tag(ref(node(t, h))) != ELS_TYPE_NULL){
        if (els_equalObj(ref, ref(node(t, h)))) return h;
        h = (h+1) % nhash(t);
    }
    return h;
}

static Node *hashnodecreate (Word nhash){
    Word i;
    Node *v = newvector (nhash, Node);
    for (i=0; i<nhash; i++)
    tag(ref(&v[i])) = ELS_TYPE_NULL;
    return v;
}

static Hash *hashcreate (Word nhash){
    Hash *t = new(Hash);
    nhash = redimension((Word)((float)nhash/REHASH_LIMIT));
    node_vec(t) = hashnodecreate(nhash);
    nhash(t) = nhash;
    nuse(t) = 0;
    markarray(t) = 0;
    return t;
}

static void hashdelete (Hash *t){
    els_vm_free (node_vec(t));
    els_vm_free(t);
}

void els_hashmark (Hash *h){
    if (markarray(h) == 0){
        Word i;
        markarray(h) = 1;
        for (i=0; i<nhash(h); i++){
            Node *n = node(h,i);
            if (tag(ref(n)) != ELS_TYPE_NULL){
                els_markobject(&n->ref);
                els_markobject(&n->val);
            }
        }
    } 
}

static void call_catchs (void){
    Hash *curr_array;
    Object t;
    tag(&t) = ELS_TYPE_ARRAY;
    for (curr_array = listhead; curr_array; curr_array = curr_array->next)
    if (markarray(curr_array) != 1){
        avalue(&t) = curr_array;
        els_vm_gc_CERROR(&t);
    }
    tag(&t) = ELS_TYPE_NULL;
    els_vm_gc_CERROR(&t); 
}

Long els_hashcollector (void){
    Hash *curr_array = listhead, *prev = NULL;
    Long counter = 0;
    call_catchs();
    while (curr_array != NULL){
        Hash *next = curr_array->next;
        if (markarray(curr_array) != 1){
           if (prev == NULL) listhead = next;
        else prev->next = next;
        hashdelete(curr_array);
        ++counter;
        }else{
            markarray(curr_array) = 0;
            prev = curr_array;
        } 
    curr_array = next;
    }
    return counter;
}

Hash *els_createarray (Word nhash){
    Hash *array;
    els_pack();
    array = hashcreate(nhash);
    array->next = listhead;
    listhead = array;
    return array;
}

static void rehash (Hash *t){
    Word i;
    Word   nold = nhash(t);
    Node *vold = node_vec(t);
    nhash(t) = redimension(nhash(t));
    node_vec(t) = hashnodecreate(nhash(t));
    for (i=0; i<nold; i++){
        Node *n = vold+i;
        if (tag(ref(n)) != ELS_TYPE_NULL && tag(val(n)) != ELS_TYPE_NULL) *node(t, present(t, ref(n))) = *n;
    }
    els_vm_free(vold);
}

Object *els_hashget (Hash *t, Object *ref)
{
    Word h = present(t, ref);
    if (tag(ref(node(t, h))) != ELS_TYPE_NULL) return val(node(t, h));
    else return NULL;
}

Object *els_hashinit (Hash *t, Object *ref){
    Word   h;
    Node *n;
    h = present(t, ref);
    n = node(t, h);
    if (tag(ref(n)) == ELS_TYPE_NULL){
        nuse(t)++;
        if ((float)nuse(t) > (float)nhash(t)*REHASH_LIMIT){
            rehash(t);
            h = present(t, ref);
            n = node(t, h);
        }
        *ref(n) = *ref;
        tag(val(n)) = ELS_TYPE_NULL;
    }
    return (val(n));
}


static void hashnext (Hash *t, Word i)
{
    if (i >= nhash(t)){
        els_pushnull(); els_pushnull();
        return;
    }
    while (tag(ref(node(t,i))) == ELS_TYPE_NULL || tag(val(node(t,i))) == ELS_TYPE_NULL){
        if (++i >= nhash(t)){
            els_pushnull(); els_pushnull();
            return;
        }
    }
    els_vm_pushobject(ref(node(t,i)));
    els_vm_pushobject(val(node(t,i)));
}

